import smtplib
from config import cfg
from email.mime.text import MIMEText
from email.header import Header
from email.utils import parseaddr, formataddr
import logging
import socket
import os
import time

class IPwatcher(object):
    def __init__(self):

        if not os.path.exists(cfg.log_file):
            self.send_email(self._get_host_IP())

        logging.basicConfig(
            filename=cfg.log_file,
            format="%(asctime)s - %(message)s",
            level=logging.INFO
        )

    def check(self):
        """监视 IP 是否变化, 若变化则发送邮件通知, 只运行一次.
        """
        print("IP address: {}".format(self._get_host_IP()))

        if not self.record_IP():
            print("IP 地址已改变！")
            ip = self._get_host_IP()
            self.send_email(ip)

    def run(self, interval=cfg.interval):
        """通过轮询的方式监视 IP 是否变化, 若变化则发送邮件通知.
        
        Args:
            interval: int, 轮询 IP 地址的间隔秒数.
        """
        while True:
            if not self.record_IP():
                ip = self._get_host_IP()
                print("IP address: {}".format(self._get_host_IP()))
                self.send_email(ip)
            time.sleep(interval)
            
    def send_email(self, ip):
        """发送 IP 地址至预定义的邮箱.

        Args:
            ip: str, ip 地址.
        """
        msg = MIMEText("机器当前的IP为：{}".format(ip), 'plain', 'utf-8')
        msg['From'] = _format_addr("IP watcher {}".format(cfg.from_addr))
        msg['To'] = _format_addr("Admin {}".format(cfg.to_addr))
        msg['Subject'] = Header("IP地址", "utf-8").encode()

        server = smtplib.SMTP(cfg.smtp_server, 25) # SMTP协议默认端口是25
        # server.set_debuglevel(1)
        server.login(cfg.from_addr, cfg.password)
        server.sendmail(cfg.from_addr, [cfg.to_addr], msg.as_string())
        server.quit()

    def record_IP(self):
        """IP 地址记录器.

        每个 interval 秒记录一次 IP 地址.
        保持日志的行数小于等于 3 行.

        Args:
            interval: int, 轮询间隔.

        Returns:
            bool, IP 是否未改变.
        """

        # 若日志行数大于 3, 则只保留最后 2 项.
        if self._count_row(cfg.log_file) >2:
            with open(cfg.log_file, 'r') as f_in:
                content = f_in.readlines()
            with open(cfg.log_file, 'w') as f_out:
                f_out.writelines(content[-2:])

        # 记录最新 IP 使得日志保持在三行
        logging.info(self._get_host_IP())

        return self._cmp_ip_log()

    def _cmp_ip_log(self, log_file=cfg.log_file):
        """对比 IP 地址日志中最后两行的 IP 是否相同.

        Args:
            log_file: str, 日志文件路径.

        Return:
            bool, IP 地址未变返回 True, 否则返回 False.
        """
        if self._count_row(log_file) <= 1:
            return True

        with open(log_file, 'r') as f:
            lines = f.readlines()
            cur_ip = lines[-1].split()[-1]
            last_ip = lines[-2].split()[-1]

            if cur_ip == last_ip:
                return True

        return False
            
    def _count_row(self, file_path):
        """计算文件的行数.

        Args:
            file_path: str, 文件路径.
        
        Returns:
            num: int, 文件行数.
        """
        num = 0

        with open(file_path, 'r') as f:
            for _ in f:
                num += 1

        return num


    def _get_host_IP(self):
        """获得本机 IP 地址。
        
        Returns:
            ip: str, IP地址。
        """
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            s.connect(('8.8.8.8', 80))
            ip = s.getsockname()[0]
        finally:
            s.close()

        return ip


def _format_addr(s):
    """格式化地址.

    Args:
        s: str, 类似"IP watcher <quanlin@seu.edu.cn>"

    Returns:
        str, 类似"'=?utf-8?q?IP_watcher?= <quanlin@seu.edu.cn>'"
    """
    name, addr = parseaddr(s)

    return formataddr((Header(name, 'utf-8').encode(), addr))


def test_count_row():
    watcher = IPwatcher()
    num = watcher._count_row("None")
    print(num)

def test_cmp_IP_log():
    watcher = IPwatcher()
    print(watcher._cmp_ip_log())

def test_record_IP():
    watcher = IPwatcher()
    watcher.record_IP()

if __name__ == '__main__':
    watcher = IPwatcher()
    watcher.check()

